Flextable Examples

Basic Tables

To create a basic table just need to set up a data frame/tibble with all the data, rows and columns you wish to display. Can simply just assign a new object using flextable() if you wish to use all columns in the data frame. Alternatively you can add the argument col_keys to select only certain columns from the data.

imdb_animation_avgs <- imdb %>%
  filter(type != "videoGame")%>%
  group_by(type, animation)%>%
  summarise(Mean.Votes = mean(numVotes, na.rm = TRUE),
            Mean.Rating = mean(averageRating, na.rm = TRUE),
            Number.Of.Entries = n())

t <- flextable(imdb_animation_avgs) #left assign a new flextable object from your data frame
t

type

animation

Mean.Votes

Mean.Rating

Number.Of.Entries

movie

FALSE

17337.709

6.254202

41085

movie

TRUE

36393.630

6.656642

1197

short

FALSE

1576.763

6.823500

1183

short

TRUE

1822.737

7.363987

933

tvMiniSeries

FALSE

5779.093

7.452980

906

tvMiniSeries

TRUE

2829.885

7.411458

96

tvMovie

FALSE

1857.997

6.093077

3019

tvMovie

TRUE

2162.584

7.100000

125

tvSeries

FALSE

12953.661

7.208266

4682

tvSeries

TRUE

7238.361

7.269250

1174

tvShort

FALSE

1929.167

7.422917

48

tvShort

TRUE

2902.484

7.179121

91

tvSpecial

FALSE

1830.817

7.427378

431

tvSpecial

TRUE

5492.600

7.820000

5

video

FALSE

2556.080

5.559368

1425

video

TRUE

5036.685

6.431738

397

Include a title and a subtitle

Adding a title and subtitle requires a little bit of logic to remember. unfortunately you can’t just add them directly like other packages. They are both added by using add_header_lines function for which you just need to specify text. However, you want to add the subtitle first as the header line always goes to the very top so you need to start at the bottom of the header and work upwards.

In this example we have also changed the font size of the main title to make it stand out from the subtitle. In flextable notation i means row numbers or names and j means column names or numbers. You also want to specify the part of the table as the rows and columns within the header, the body of the table and the footer are treated separately. So in this case the title is row 1 of the header.

top_20_movies <- imdb %>%
  filter(type == "movie" & numVotes > 99999)%>%
  arrange(desc(averageRating))%>%
  select(title, year, averageRating, numVotes)%>%
  slice(1:20)


t <- flextable(top_20_movies)
t <- add_header_lines(t, values = "IMDB Entries with at least 100,000 votes") # add subtitle
t <- add_header_lines(t, values = "Top 20 Movies of all time") # add title
t <- fontsize(t, i = 1, size = 14, part = "header") #increase text size of the title 
t <- autofit(t) # autofit the width of the table and columns
t

Top 20 Movies of all time

IMDB Entries with at least 100,000 votes

title

year

averageRating

numVotes

The Shawshank Redemption

1994

9.3

2138866

The Godfather

1972

9.2

1468315

The Godfather: Part II

1974

9.0

1021652

The Dark Knight

2008

9.0

2102907

The Mountain II

2016

9.0

101445

12 Angry Men

1957

8.9

610262

Schindler's List

1993

8.9

1109973

Pulp Fiction

1994

8.9

1678715

The Lord of the Rings: The Return of the King

2003

8.9

1520719

The Good, the Bad and the Ugly

1966

8.8

635197

Forrest Gump

1994

8.8

1646150

The Lord of the Rings: The Fellowship of the Ring

2001

8.8

1536315

Fight Club

1999

8.8

1709679

Inception

2010

8.8

1875664

One Flew Over the Cuckoo's Nest

1975

8.7

845858

Star Wars: Episode V - The Empire Strikes Back

1980

8.7

1070168

Goodfellas

1990

8.7

924279

The Matrix

1999

8.7

1540221

The Lord of the Rings: The Two Towers

2002

8.7

1375568

It's a Wonderful Life

1946

8.6

363682

change column labels and include number formatting

Changing the labels of columns is quite simple and takes the same format as the rename function from dplyr. You simply write variable.name = "new column name".

You can set the formatting of a numeric column using colformat_num by specifying the column you wish to format (j = 4 in this example). You can then set the number of decimal places, the big mark, a string for missing values, and also add any prefixing or suffixing if you wish to specify the units directly.

t <- flextable(top_20_movies)
t <- set_header_labels(t, 
                       title = "Movie",
                       year = "Year Released",
                       averageRating = "Rating",
                       numVotes = "Total Votes") # written in same fashion as dplyr::rename (variable.name = "new column name")
t <- colformat_num(t, 
                   j = 4, # column number 4
                   digits = 0, # no decimal places
                   big.mark = ",") # use comas when dealing with large numbers
t <- add_header_lines(t, values = "IMDB Entries with at least 100,000 votes") # add subtitle
t <- add_header_lines(t, values = "Top 20 Movies of all time") # add title
t <- fontsize(t, i = 1, size = 14, part = "header") #increase text size of the title
t <- autofit(t)
t

Top 20 Movies of all time

IMDB Entries with at least 100,000 votes

Movie

Year Released

Rating

Total Votes

The Shawshank Redemption

1994

9.3

2,138,866

The Godfather

1972

9.2

1,468,315

The Godfather: Part II

1974

9.0

1,021,652

The Dark Knight

2008

9.0

2,102,907

The Mountain II

2016

9.0

101,445

12 Angry Men

1957

8.9

610,262

Schindler's List

1993

8.9

1,109,973

Pulp Fiction

1994

8.9

1,678,715

The Lord of the Rings: The Return of the King

2003

8.9

1,520,719

The Good, the Bad and the Ugly

1966

8.8

635,197

Forrest Gump

1994

8.8

1,646,150

The Lord of the Rings: The Fellowship of the Ring

2001

8.8

1,536,315

Fight Club

1999

8.8

1,709,679

Inception

2010

8.8

1,875,664

One Flew Over the Cuckoo's Nest

1975

8.7

845,858

Star Wars: Episode V - The Empire Strikes Back

1980

8.7

1,070,168

Goodfellas

1990

8.7

924,279

The Matrix

1999

8.7

1,540,221

The Lord of the Rings: The Two Towers

2002

8.7

1,375,568

It's a Wonderful Life

1946

8.6

363,682

Add borders

Adding borders requires the addition of another package called officer. Using this package you create what is known as a border properties object by setting its colour, style and width e.g. border_h = fp_border(color="blue", style = "dotted", width = 3) will create a a thick blue dotted border that can be used within your flextable

border_h = fp_border(color="blue", style = "dotted", width = 3)
border_o = fp_border(color = "red", style = "solid", width = 2)

t <- border_inner(t, part = "body", border = border_h)   #SET BORDER FOR EVERY ROW AND COLUMN INSIDE THE TABLE
t <- border_outer(t, border = border_o, part = "body") # SET OUTER BORDER
#t <- border_inner_h(t, part = "body", border = border_h) ONLY HORIZONTAL BORDERS WITHIN THE BODY OF THE TABLE
#t <- border_inner_v(t, part = "body", border = border_h) ONLY SET VERTICAL BORDERS WITHIN THE BODY OF THE TABLE
#t <- hline(t, i = ..., j = ..., part = ..., border = ...)  SET HORIZONTAL BORDERS ON SPECIFIC ROWS/COLUMNS (BOTTOM OF CELL)
#t <- vline(t, i = ..., j = ..., part = ..., border = ...)  SET VERTICAL BORDERS ON SPECIFIC ROWS/COLUMNS (RIGHT OF CELL)
t

Top 20 Movies of all time

IMDB Entries with at least 100,000 votes

Movie

Year Released

Rating

Total Votes

The Shawshank Redemption

1994

9.3

2,138,866

The Godfather

1972

9.2

1,468,315

The Godfather: Part II

1974

9.0

1,021,652

The Dark Knight

2008

9.0

2,102,907

The Mountain II

2016

9.0

101,445

12 Angry Men

1957

8.9

610,262

Schindler's List

1993

8.9

1,109,973

Pulp Fiction

1994

8.9

1,678,715

The Lord of the Rings: The Return of the King

2003

8.9

1,520,719

The Good, the Bad and the Ugly

1966

8.8

635,197

Forrest Gump

1994

8.8

1,646,150

The Lord of the Rings: The Fellowship of the Ring

2001

8.8

1,536,315

Fight Club

1999

8.8

1,709,679

Inception

2010

8.8

1,875,664

One Flew Over the Cuckoo's Nest

1975

8.7

845,858

Star Wars: Episode V - The Empire Strikes Back

1980

8.7

1,070,168

Goodfellas

1990

8.7

924,279

The Matrix

1999

8.7

1,540,221

The Lord of the Rings: The Two Towers

2002

8.7

1,375,568

It's a Wonderful Life

1946

8.6

363,682

Special Features

There are many special features of FlexTable that may not be necessarily needed most of the time but are worth highlighting though they are certainly for someone wishing to do something a bit more advanced.

Most of this revolves around the compose() function when used in combination with as_paragraph(). This allows you to manually edit text within the table as well as change who the data is visualised including by adding mini bars, line ranges and even adding image files.

border_h = fp_border(color="blue")

t <- flextable(random_movies,
               col_keys = c("title", "year", "length", "numVotes", "averageRating", "director")) # use col keys to sepcify columns to plot

t <- add_header_lines(t, values = "IMDB Entries with at least 100,000 votes") # add subtitle

t <- add_header_lines(t, values = "20 Movies") # add title

t <- flextable::align(t, i = 1:2, align = "center", part = "header") # centre align the title and sub title

t <- fontsize(t, size = 12, part = "header") # increase text size of header

t <- colformat_int(t, j = 4, big.mark = ",") # format numbers

t <- flextable::compose(t,
             j = 5,
             value = as_paragraph(
              linerange(value = averageRating)), # default minimum = minum of value and maximum = maximum of value
             part = "body") # turn average rating into a line range visual data representation

t <- flextable::compose(t,
                        j = 3,
                        value = as_paragraph(minibar(value = length, max = max(length))), # default minimum = minum of value and maximum = maximum of value
                        part = "body") # turn length into a mini bar data respresentation


# SET DIRECTORS TO BE IMAGE FILES RATHER THAN THE TEXT
t <- flextable::compose(t,
             i = ~ title == "Chef", # can conditionally set columns and row indexes as well e.g. row where title == "Chef"
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/jon favreau.jpg", width = 2, height = 2))) # additionally specify as_image
# provide a file link to the image
# set width and height of the image
t <- flextable::compose(t,
             i = c(1,8), 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/joel schumacher.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 2, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Jay_Roach.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 3, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Gabor_Csupo.png", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 5, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Ryan_Coogler.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 6, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Alex_Proyas.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 7, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Todd_Phillips.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 9, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Terry_Gilliam.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = c(10, 15), 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Guillermo-del-Toro-2017.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 11, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Eric_Brevig.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 12, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/JUDDPORTRAIT.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 13, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/220px-Dan_Scanlon.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 14, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Nicolas.png", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 16, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Wes_Craven.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 17, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Nancy_Meyers.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 18, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Phillip_Noyce.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 19, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Edward_Zwick.jpg", width = 2, height = 2)))
t <- flextable::compose(t,
             i = 20, 
             j = 6,
            value = as_paragraph(flextable::as_image(src = "directors/Iain_Softley.jpg", width = 2, height = 2)))

t <- width(t, width = 2) # manually set column width

t<-border_inner_h(t, part="body", border = border_h ) # set an interanal border

t

20 Movies

IMDB Entries with at least 100,000 votes

title

year

length

numVotes

averageRating

director

8MM

1999

116,986

Austin Powers in Goldmember

2002

186,300

Bridge to Terabithia

2007

133,561

Chef

2014

178,322

Creed

2015

229,491

Dark City

1998

179,019

Due Date

2010

307,343

Falling Down

1993

157,763

Fear and Loathing in Las Vegas

1998

245,293

Hellboy

2004

293,763

Journey to the Center of the Earth

2008

106,058

Knocked Up

2007

335,238

Monsters University

2013

297,753

Only God Forgives

2013

101,803

Pacific Rim

2013

448,115

Scream 2

1997

146,336

Something's Gotta Give

2003

104,880

The Bone Collector

1999

144,080

The Last Samurai

2003

374,818

The Skeleton Key

2005

100,935

Cotton Connect Example

The cotton connect example shown in the seminar showed off how to group rows together with a title for this sub group. This can be done so long as you have a column in your data frame which defines these groups. You can then sue the as_grouped_data() function before you create a flextable to set up this grouped data structure which will then create these groupings within the data frame. Think of it like the group_by function in dplyr but much more visual. If you look at your data frame after running this function you will see what it does exactly.

  A <- as_grouped_data(x = A,
                       groups = c("Group")) # group our data on the Group column

  A[is.na(A)] = " " #repalce NAs with an emmpty string
  
  border_h = fp_border(color="gray") # create a border object
  
  t<-flextable(A)
  t<-set_header_labels(t, values = list(Group = "Group", 
                                       `Sub-Group` = "Sub-group",
                                       `Mean (Farm` = str_wrap("Farm Area (Mean)",7), 
                                       `Median (Farm)` = str_wrap("Farm Area (Median)",7),
                                      `Mean (Cottton)` = str_wrap("Cotton Area (Mean)",7),
                                    `Median (Cotton)` = str_wrap("Cotton Area (Median)",7),
                                    `Mean (%Cotton)` = str_wrap("Cotton Area as % of Farm Area (Mean)",12), 
                                    `Median (%Cotton)` = str_wrap("Cotton Area as % of Farm Area (Median)",12),
                                    N = "N"))
  t<-fontsize(t, size = 9, part = "all")
  t<-width(t, width = 0.90)
  t<-add_header_lines(t, values = "Farm Area (Acre), Cotton Area (Acre), Cotton Area as % of Farm Area")
  t <- border_inner_h(t, part="body", border = border_h )
  t

Farm Area (Acre), Cotton Area (Acre), Cotton Area as % of Farm Area

Group

Sub-group

Mean (Farm)

Farm
Area
(Median)

Cotton
Area
(Mean)

Cotton
Area
(Median)

Cotton Area
as % of Farm
Area (Mean)

Cotton Area
as % of
Farm Area
(Median)

N

State

Gujarat

4.5

4.05

4.06

4

90.1

100

910

Madhya Pradesh

3.99

4

2.42

2

62.2

60

7833

Maharashtra

2.08

1

1.83

1

95.5

100

4914

Rajasthan

5.58

5.58

2.12

2

38.7

37.5

2532

Partner

Gujarat - Mahiti

4.22

4.03

3.63

4

86.3

100

650

Gujarat - VRTI

5.2

5.05

5.15

5

99.8

100

260

Maharashtra - SIED

1.22

1

1.2

1

98.9

100

3529

Maharashtra - Tirupati

4.26

3.5

3.45

3

87.1

100

1385

MP - Pratibha

3.97

3.04

2.35

2

59.3

57.1

3701

MP - Prerana

4

4

2.49

2

64.9

66.7

4132

Rajasthan - Pratibha

5.58

5.58

2.12

2

38.7

37.5

2532

Overall

Total

3.69

3.04

2.29

2

70.2

77.8

16189

Modelling Examples

It is additionally not too difficult to create modelling tables using flextable. You could combine it with the very useful function tidy() from the broom packages which turns model summaries into a convenient tidy data fame that can be used as a table. This tidy function can be used for numerous different types of statistical models.

Additionally however, with any glm, lm and some statistical tests you can directly pipe in the as_flextable() function and this will automatically create a model table that includes significance codes and footnotes on various model characteristics such as the R-squared and F-statistic.

movies <- imdb%>%
  filter(type == "movie")%>%
  mutate(num_Votes_10000 = numVotes/10000)

model1 <- aov(averageRating ~ length + num_Votes_10000 + animation, movies)

m1 <- broom::tidy(model1)

t <- flextable(m1)
t <- set_header_labels(t, values = list(
  term = "Term",
  df = "Df",
  sumsq = "SS",
  meansq = "MSS",
  statistic = "F - Statistic",
  p.value = "P.Value"
))
t <- colformat_num(t, j = c(3:6), digits = 2, na_str = " ")
t <- autofit(t)
t

Term

Df

SS

MSS

F - Statistic

P.Value

length

1

3,709.75

3,709.75

2,834.57

0.00

num_Votes_10000

1

1,169.75

1,169.75

893.79

0.00

animation

1

339.00

339.00

259.03

0.00

Residuals

42278

55,331.32

1.31

lm(averageRating ~ length + num_Votes_10000 + animation, movies) %>%
  as_flextable()

Estimate

Standard Error

t value

Pr(>|t|)

(Intercept)

4.925

0.026

187.775

0.0000

***

length

0.012

0.000

49.998

0.0000

***

num_Votes_10000

0.023

0.001

28.857

0.0000

***

animationTRUE

0.544

0.034

16.094

0.0000

***

Signif. codes: 0 <= '***' < 0.001 < '**' < 0.01 < '*' < 0.05 < '.' < 0.1 < '' < 1

Residual standard error: 1.144 on 42278 degrees of freedom

Multiple R-squared: 0.08619, Adjusted R-squared: 0.08612

F-statistic: 1329 on 42278 and 4 DF, p-value: 0.0000

t.test(averageRating ~ fantasy, data = movies)%>%
  as_flextable()

estimate

estimate1

estimate2

statistic

p.value

parameter

conf.low

conf.high

method

alternative

0.293

6.280625

5.987644

10.352

0.000

2372.144

0.2374828

0.3484785

Welch Two Sample t-test

two.sided